#include "asm/includes.h"


#define uart_clk            clk_get("uart")
#define UART_RXBUF_SIZE     128

u8 uart_pin[3][8] = {
    //uart0   IOMAP_CON0[7:6]
    {
        IO_PORTA_05, IO_PORTA_06, //tx rx
        IO_PORTB_04, IO_PORTB_06,
        IO_PORTB_05, IO_PORTB_05,
        IO_PORTA_11, IO_PORTA_12
    },
    //uart1   IOMAP_CON1[3:2]
    {
        IO_PORTB_00, IO_PORTB_01, //tx rx
        IO_PORTC_00, IO_PORTC_01,
        IO_PORTA_00, IO_PORTA_01,
        IO_PORT_DP, IO_PORT_DM
    },
    //uart2   IOMAP_CON1[15:14]
    {
        IO_PORTA_02, IO_PORTA_03, //tx rx
        IO_PORTA_09, IO_PORTA_10,
        IO_PORTB_09, IO_PORTB_10,
        IO_PORTC_04, IO_PORTC_05
    }
};
struct uart_handle {
    u8 irq_num;
    u8 irq_priority;
    JL_UART_TypeDef *reg;
    void (*irq_handler)();
};


static void uart0_irq_handler();
static void uart1_irq_handler();

static u8 uart_rxbuf[2][UART_RXBUF_SIZE] __attribute__((aligned(32)));
static void (*tx_callback[2])();
static void (*irq_user_handler[2])(u8 *rxbuf, u16 len) = {NULL, NULL};

static const struct uart_handle uart_hdl[2] = {
    [0] = {
        .irq_num            = IRQ_UART0_IDX,
        .irq_priority       = 0,
        .reg                = JL_UART0,
        .irq_handler        = uart0_irq_handler,
    },

    [1] = {
        .irq_num            = IRQ_UART1_IDX,
        .irq_priority       = 0,
        .reg                = JL_UART1,
        .irq_handler        = uart1_irq_handler,
    },
};


static void uart_irq_handler(int id)
{
    JL_UART_TypeDef *reg = uart_hdl[id].reg;

    // RX irq
    if ((reg->CON0 & BIT(5)) && (reg->CON0 & BIT(11))) {
        reg->CON0 |= BIT(12) | BIT(10) | BIT(7);
        __asm__ volatile("csync");

        if (irq_user_handler[id]) {
            irq_user_handler[id](uart_rxbuf[id], reg->HRXCNT);
        }

        reg->RXSADR  = (u32)uart_rxbuf[id];
        reg->RXEADR  = (u32)(uart_rxbuf[id] + UART_RXBUF_SIZE);
        reg->RXCNT = UART_RXBUF_SIZE;
    }

    // TX irq
    if ((reg->CON0 & BIT(2)) && (reg->CON0 & BIT(15))) {
        reg->CON0 |= BIT(13);
        if (tx_callback[id]) {
            tx_callback[id]();
        }
    }
}

___interrupt
static void uart0_irq_handler()
{
    uart_irq_handler(0);
}

___interrupt
static void uart1_irq_handler()
{
    uart_irq_handler(1);
}



int uart_io_init(u8 uart_id, const struct uart_platform_data *data)
{
    if (uart_id > 2) {
        printf("uart dev error\n");
        return -ENODEV;
    }
    u8 i = 0;
    u8 ut_ch = (u8) - 1;
    u8 gpio_input_channle_flag = 0;
    u8 ut_chx_sel = data->tx_ch_sel;
    if (uart_id != data->id) {
        if (ut_chx_sel != 0xff) {
            if ((uart_id == 1) && ((ut_chx_sel & 0x0f) != 1)) {
                printf("uart%d OUTPUT_CH_SEL select error\n", uart_id);
                /* ut_chx_sel=(ut_chx_sel &0xf0)+1;//自动分配时通道错误会匹配初始化相应通道 */
                ut_chx_sel = CH0_UT1_TX; //自动分配时通道错误会改为用通道0
                /* return  -ENODEV;//关闭自动分配 */
            }
            if ((uart_id == 0) && ((ut_chx_sel & 0x0f) != 0)) {
                printf("uart%d OUTPUT_CH_SEL select error\n", uart_id);
                ut_chx_sel = CH0_UT0_TX; //自动分配时通道错误会改为用通道0
                /* return  -ENODEV;//关闭自动分配 */
            }
            if ((uart_id == 2) && ((ut_chx_sel & 0x0f) != 7)) {
                printf("uart%d OUTPUT_CH_SEL select error\n", uart_id);
                return  -ENODEV;//关闭自动分配
            }
        }
    }
    for (i = 0; i < 8; i += 2) {
        if (data->tx_pin == uart_pin[uart_id][i]) {
            ut_ch = i / 2;
            if ((data->rx_pin != uart_pin[uart_id][i + 1]) && (data->rx_pin < IO_PORT_MAX)) {
                gpio_input_channle_flag = 1;
            }
            break;
        } else if (data->tx_pin == 0xff) {
            if (data->rx_pin == uart_pin[uart_id][i + 1]) {
                ut_ch = i / 2;
                break;
            } else if (data->rx_pin == 0xff) {
                printf("uart tx rx error\n");
                return  -ENODEV;//pin error
            }
        } else {
            if (data->rx_pin == uart_pin[uart_id][i + 1]) {
                ut_ch = i / 2;
                if (ut_chx_sel != 0xff) {
                    gpio_output_channel(data->tx_pin, ut_chx_sel);
                } else {
                    printf("uart tx OUTPUT_CH_SEL select error\n");
                }
                break;
            } else if (data->rx_pin == 0xff) {
                break;
            }
        }
    }
    if (uart_id == 2) {
        gpio_set_uart2(ut_ch);
    } else if (uart_id == 1) {
        gpio_set_uart1(ut_ch);
    } else {
        gpio_set_uart0(ut_ch);
    }
    if (ut_ch == 0xff) {
        if (data->tx_pin != 0xff) {
            if (ut_chx_sel != 0xff) {
                gpio_output_channel(data->tx_pin, ut_chx_sel);
            } else {
                printf("uart tx OUTPUT_CH_SEL select error\n");
            }
        }
        if (data->rx_pin != 0xff) {
            gpio_uart_rx_input_channel(data->rx_pin, uart_id, data->rx_ch_sel);
        }
    }
    if (gpio_input_channle_flag) {
        gpio_uart_rx_input_channel(data->rx_pin, uart_id, data->rx_ch_sel);
    }
    return 0;
}
int uart_init(const struct uart_platform_data *data)
{
    static u8 uart_inited = 0;
    int id = data->id;
    const struct uart_handle *hdl = NULL;

    if (uart_inited == 0) {
        uart_inited = 1;
        for (int i = 0; i < ARRAY_SIZE(uart_hdl); i++) {
            uart_hdl[i].reg->CON0 = 0;
        }
    }

    if (id < 2) {
        if (uart_hdl[id].reg->CON0 & BIT(0)) {
            return -EBUSY;
        }
        hdl = &uart_hdl[id];
    } else {
        for (id = 0; id < ARRAY_SIZE(uart_hdl); id++) {
            if (!(uart_hdl[id].reg->CON0 & BIT(0))) {
                hdl = &uart_hdl[id];
                break;
            }
        }
        if (!hdl) {
            return -ENODEV;
        }
    }

    JL_UART_TypeDef *reg = hdl->reg;

    reg->CON0 = BIT(13) | BIT(12) | BIT(10);
    reg->BAUD = (uart_clk / data->baudrate) / 4 - 1;

    request_irq(hdl->irq_num, hdl->irq_priority, hdl->irq_handler, 0);

    if (data->rx_pin < IO_PORT_MAX) {
        reg->RXSADR  = (u32)uart_rxbuf[id];
        reg->RXEADR  = (u32)(uart_rxbuf[id] + UART_RXBUF_SIZE);
        reg->RXCNT = UART_RXBUF_SIZE;
        reg->OTCNT = clk_get("lsb") / 1000; //Time(ot)= Time(lsb) * UTx_OTCNT
        reg->CON0 |= BIT(5);   //OT中断
        reg->CON0 |= BIT(6);   //启动DMA
        /* reg->CON0 |= BIT(1);   //RX enable */
    }

    if (uart_io_init(id, data) < 0) {
        printf("uart io init error\n");
        return -ENODEV;
    }

    reg->CON0 |= BIT(0); //TX enable

    return id;
}

void uart_set_rx_irq_handler(int id, void (*handler)(u8 *, u16))
{
    ASSERT(id < 2, "id = %d\n", id);

    irq_user_handler[id] = handler;
}

void uart_tx_byte(int id, u8 byte)
{
    JL_UART_TypeDef *reg = uart_hdl[id].reg;

    reg->CON0 &= ~BIT(2);       //关闭发送中断
    reg->BUF = byte;
    __asm__ volatile("csync");
    while ((reg->CON0 & BIT(15)) == 0);
    reg->CON0 |= BIT(13);  //清Tx pending
}

void uart_tx_buf(int id, u8 *txdata, u16 len)
{
    JL_UART_TypeDef *reg = uart_hdl[id].reg;

    reg->CON0 &= ~BIT(2);       //关闭发送中断
    reg->TXADR = (u32)txdata;
    reg->TXCNT = len;
    __asm__ volatile("csync");

    while ((reg->CON0 & BIT(15)) == 0);
    reg->CON0 |= BIT(13);  //清Tx pending
}

void uart_async_tx_buf(int id, u8 *txdata, u16 len, void (*callback)())
{
    JL_UART_TypeDef *reg = uart_hdl[id].reg;

    tx_callback[id] = callback;

    reg->CON0 |= BIT(13);  //清Tx pending
    reg->CON0 |= BIT(2);   //使能发送中断
    reg->TXADR = (u32)txdata;
    reg->TXCNT = len;
}

u32 uart_close(int id, const struct uart_platform_data *data)
{
    JL_UART_TypeDef *reg = uart_hdl[id].reg;
    u8 ch;
    if (id < 2) { //uart2 用于打印
        if (reg->CON0 & BIT(0)) {
            irq_disable(uart_hdl[id].irq_num);
            reg->CON0 = BIT(13) | BIT(12) | BIT(10);
            if (id == 0) {
                JL_IOMAP->CON3 &= ~(0b1111 << 0);
            } else if (id == 1) {
                JL_IOMAP->CON3 &= ~(0b1111 << 4);
            }
            /* else if(id==2){ */
            /* JL_IOMAP->CON3 &= ~(0b1111 << 4); */
            /* } */
            gpio_set_direction(data->tx_pin, 1);
            gpio_set_die(data->tx_pin, 0);
            gpio_set_direction(data->rx_pin, 1);
            gpio_set_pull_up(data->rx_pin, 0);
            gpio_set_die(data->rx_pin, 0);
            printf("uart%d close! CON0:%x\n", id, reg->CON0);
        }
        return 0;
    } else {
        return 1;
    }
}


/**************************************uart test 例程**********************************/
/*
 * 小机发送uart_test_txbuf缓存数据到pc, pc发送数据到小机, pc发送间隔10ms,
 * 包大小64字节以内，小机接收完检查rx/tx数据是否一样，打印成功或失败
 * */
#if UART_TEST_EN

static const struct uart_platform_data uart_config = {
    .id         = 1,//0xff,      //成员说明见结构体定义
    .tx_pin     = IO_PORTC_00,
    .tx_ch_sel = CH0_UT0_TX,//0xff,//发送IO通道选择,选择enum OUTPUT_CH_SEL，不使用时赋值0xff
    .rx_pin     = IO_PORTC_02,
    .rx_ch_sel = 0,//接收IO通道选择,取值范围0,1,2,3对应输入通道0~3
    .baudrate   = 1000000,
};

static volatile u8 uart_test_rx_flag = 0;
static volatile u8 uart_test_tx_flag = 0;

static u8 uart_test_rxbuf[64 + 16];
static u8 uart_test_txbuf[64] __attribute__((aligned(4)));

static void uart_loop_rx_handler(u8 *rxdata, u16 len)
{
    memcpy(uart_test_rxbuf, rxdata, len);
    uart_test_rx_flag = 1;
}

static void loop_tx_callback()
{
    uart_test_tx_flag = 1;
}

void uart_loop_test()
{
    int id = uart_init(&uart_config);

    printf("uart%d test!\n", id);
    uart_set_rx_irq_handler(id, uart_loop_rx_handler);

    for (int i = 0; i < sizeof(uart_test_txbuf); i++) {
        uart_test_txbuf[i] = i;
    }

    puts("---------sync dma tx test --------\n");

    for (int j = 0; j < 100; j++) {
        uart_test_rx_flag = 0;
        uart_tx_buf(id, uart_test_txbuf, sizeof(uart_test_txbuf));

        while (1) {
            if (uart_test_rx_flag) {
                break;
            }
            wdt_clear();
        }
        if (!memcmp(uart_test_rxbuf, uart_test_txbuf, sizeof(uart_test_txbuf))) {
            printf("sync loop test suss: %d\n", j);
            memset(uart_test_rxbuf, 0, sizeof(uart_test_rxbuf));
        } else {
            puts("sync loop test faild:\n");
            put_buf(uart_test_rxbuf, sizeof(uart_test_rxbuf));
            return;
        }
    }

    puts("---------async dma tx test --------\n");

    for (int j = 0; j < 100; j++) {
        uart_test_rx_flag = 0;
        uart_test_tx_flag = 0;
        uart_async_tx_buf(id, uart_test_txbuf, sizeof(uart_test_txbuf), loop_tx_callback);

        while (1) {
            if (uart_test_rx_flag && uart_test_tx_flag) {
                break;
            }
            wdt_clear();
        }
        if (!memcmp(uart_test_rxbuf, uart_test_txbuf, sizeof(uart_test_txbuf))) {
            printf("async loop test suss: %d\n", j);
            memset(uart_test_rxbuf, 0, sizeof(uart_test_rxbuf));
        } else {
            puts("async loop test faild:\n");
            put_buf(uart_test_rxbuf, sizeof(uart_test_rxbuf));
            return;
        }
    }
    uart_close(id, &uart_config);
}

#endif



